package cn.lanxin.controller;


import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * @author dell
 * @date 2021-01-28 23:29
 * @description
 */

@RestController
@RequestMapping("/autodeliver")
public class AutodeliverController {
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;//注入服务发现客户端

    @GetMapping("checkAndBegin/{userId}")
    public Integer findResumeOpenState(@PathVariable long userId){
        //获取服务列表
        List<ServiceInstance> serviceInstances = discoveryClient.getInstances("service-resume");
        //此处取第一个没有使用负载均衡
        ServiceInstance serviceInstance = serviceInstances.get(0);
        //根据实例拼接地址
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        String url = "http://"+host+":"+port+"/resume/openstate/"+userId;
        //消费者直接调用服务者 Eureka的元数据有两种：标准元数据和⾃定义元数据
        Integer forObject = restTemplate.getForObject(url, Integer.class);
        System.out.println("forObject:"+forObject);
        return forObject;
    }

    /**
     * @desc 使⽤Ribbon负载均衡
     * @param userId
     * @return
     */
    @GetMapping("/checkStateTimeout/{userId}")
    public Integer checkStateTimeout(@PathVariable Long userId) {
        // 使⽤ribbon不需要我们⾃⼰获取服务实例然后选择⼀个那么去访问了（⾃⼰的负载均衡）
        String url = "http://service-resume/resume/openstate/" + userId; // 指定服务名
        Integer forObject = restTemplate.getForObject(url, Integer.class);
        return forObject;
    }

    /**
     * @desc 提供者模拟处理超时，调⽤⽅法添加Hystrix控制 使⽤@HystrixCommand注解进⾏熔断控制
     * @param userId
     * @return
     */
    @HystrixCommand(
            //线程池标识，要保持唯一，不唯一就会公用
            threadPoolKey = "findResumeOpenStateTimeout",
            //线程池细节属性配置
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize",value = "1"),//线程数
                    @HystrixProperty(name = "maxQueueSize",value = "20")//等待队列的长度
            },
            commandProperties = {
                    //细节配置，每一个属性都是HystrixProperty/**
                    @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="2000"),
                    //高级配置
                    // * 8秒钟内，
                    // * 请求次数达到2个，
                    // * 并且失败率在50%以上，就跳闸
                    // * 跳闸后活动窗⼝设置为3s
                    // */
                    //1.统计时间窗口定义
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "8000"),
                    //2.统计时间窗口内的最小请求数
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "2"),
                    //统计时间窗口内的错误数量百分比阈值
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50"),
                    //自我修复的活动窗口长度
                    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "3000")
            },
            fallbackMethod = "myFallBack"//回退方法
    )
    @GetMapping("/findResumeOpenStateTimeout/{userId}")
    public Integer findResumeOpenStateTimeout(@PathVariable Long userId) {
        // 使⽤ribbon不需要我们自己获取服务实例然后选择⼀个那么去访问了（自己的负载均衡）
        String url = "http://service-resume/resume/openstate/" + userId; // 指定服务名
        Integer forObject = restTemplate.getForObject(url, Integer.class);
        return forObject;
    }

    /*
    *定义回退⽅法，返回预设默认值
    * 1）服务提供者处理超时，熔断，返回错误信息
    * 2）有可能服务提供者出现异常直接抛出异常信息
    * 这些信息如果抛给⽤户很不友好，此时，我们可以返回⼀个兜底数据，预设的默认值（服务降级）
    * 注意：该⽅法形参和返回值与原始⽅法保持⼀致
    */
    public Integer myFallBack(Long userId) {
        return -123333; // 兜底数据
    }





}
